import React, { createContext, ReactElement } from 'react';
import { filter, pipe, reduce } from 'ramda';

import { State, Action, Context, LiveUser } from './types';
import actions from './actions';
import Provider from '../../components/Provider';
import {
  ClassRoomType,
  PPTStatus,
  PPTType,
  RoundType,
  ShowToastType
} from './constant';
import { RoundInfo } from '../../api/class/getRoundList';

export const initialState: State = {
  lesson: {
    lessonId: -1,
    lessonName: '',
    lessonStartTime: '',
    lessonResourceId: 0,
    lessonSource: ''
  },
  channel: '',
  users: [],
  showLiveEnd: false,
  showProblemResult: false,
  showDownloadResource: true,
  showProblemWait: false,
  showPrepare: false,
  rounds: [],
  isRequestFailed: false,
  isJoinedChannel: false,
  problemResult: {
    problemId: 0,
    withSmall: false,
    isCorrect: false,
    results: [],
    addGoldCoinNum: 0
  },
  mustDownloadVideoUrls: [],
  needDownloadVideoNum: 0,
  type: ClassRoomType.Live,
  isOpenMicrophone: false,
  welcomeData: {
    startImageURL: '',
    startVideoURL: ''
  },
  instantAnswerInfo: {
    show: false,
    instantAnswer: {
      instantAnswerId: -1,
      startTime: 0,
      duration: 0,
      isDefault: false,
      options: [],
      answer: '',
      title: {
        textType: 0,
        textStr: ''
      }
    }
  },
  isNeedPreloadResource: true,
  totalTime: 0,
  currentTalkingUsers: [],
  showOpenMicrophone: false,
  PPTList: [],
  currentPPT: {
    type: PPTType.Unknown,
    index: -1,
    roundId: -1,
    status: PPTStatus.Unknown,
    lessonId: -1
  },
  classRoomTotalTime: 0,
  classRoomChangeTime: -1,
  showLoading: false,
  showContainerLoading: false,
  showBar: false,
  showToastType: ShowToastType.Unknown,
  showSignIn: false,
  showSignSuccess: false,
  needSign: false,
  assistantInfo: {
    id: 0,
    headUrl: '',
    name: ''
  },
  soundUrlList: [],
  lessonStartTime: '',
  showLock: false,
  isMute: false,
  teacherName: '',
  parentStudentId: ''
};

export const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'INIT_CLASS_ROOM': {
      const {
        lessonId,
        channel,
        type,
        lessonResourceId,
        welcomeData,
        intervalBeforeLessonStart,
        lessonName,
        lessonSource,
        needSign,
        assistantInfo,
        lessonStartTime
      } = action.value;
      const { showLock } = state;
      return {
        ...state,
        lesson: {
          lessonId,
          lessonResourceId,
          lessonName,
          lessonSource,
          lessonStartTime: ''
        },
        channel,
        type,
        welcomeData,
        isNeedPreloadResource: intervalBeforeLessonStart < 5 * 60 * 1000,
        needSign,
        assistantInfo,
        lessonStartTime,
        isMute: showLock || needSign
      };
    }
    case 'SET_USERS': {
      const users = action.value.map(
        (item): LiveUser => ({
          ...item,
          userId: String(item.userId)
        })
      );
      return {
        ...state,
        users
      };
    }
    case 'SET_TALKING_USERS': {
      return { ...state, currentTalkingUsers: action.value };
    }
    case 'SET_SHOW_LIVE_END': {
      return {
        ...state,
        showLiveEnd: action.value
      };
    }
    case 'SET_SHOW_PROBLEM_RESULT': {
      return {
        ...state,
        showProblemResult: action.value,
        problemResult: action.value
          ? state.problemResult
          : {
              problemId: 0,
              withSmall: false,
              isCorrect: false,
              results: [],
              addGoldCoinNum: 0
            }
      };
    }
    case 'SET_SHOW_DOWNLOAD_RESOURCE': {
      return {
        ...state,
        showDownloadResource: action.value
      };
    }
    case 'SET_SHOW_PROBLEM_WAIT': {
      return {
        ...state,
        showProblemWait: action.value
      };
    }
    case 'SET_SHOW_PREPARE': {
      return {
        ...state,
        showPrepare: action.value
      };
    }
    case 'UPDATE_ROUNDS': {
      return { ...state, rounds: action.value };
    }
    case 'SET_ROUNDS': {
      const totalTime = reduce(
        (left: number, right: RoundInfo): number => left + right.duration,
        0
      );
      const validRounds = pipe(
        (roundList: RoundInfo[]): RoundInfo[] =>
          filter((round: RoundInfo): boolean => {
            const { roundType, roundInfoVideo } = round;
            const isValidProblemRound = roundType === RoundType.Problem;

            const isValidVideo = !!(
              roundInfoVideo &&
              roundInfoVideo.video &&
              roundInfoVideo.video.originVideoDownloadURL
            );

            const isValidRollCall = roundType === RoundType.RollCall;

            const validRound =
              isValidProblemRound || isValidVideo || isValidRollCall;

            return validRound;
          }, roundList),
        (roundList: RoundInfo[]): RoundInfo[] =>
          roundList.map(
            (round: RoundInfo, index): RoundInfo => {
              const aboveRoundList = roundList.filter(
                (r, i): boolean => i < index
              );
              const aboveTotalTime = totalTime(aboveRoundList);

              return { ...round, aboveTotalTime };
            }
          )
      )(action.value);

      const classRoomTotalTime = totalTime(validRounds);
      return {
        ...state,
        rounds: validRounds,
        classRoomTotalTime
      };
    }
    case 'SET_IS_JOINED_CHANNEL':
      return { ...state, isJoinedChannel: action.value };
    case 'SET_TOTAL_TIME':
      return { ...state, totalTime: action.value };
    case 'SET_PROBLEM_RESULT':
      return { ...state, problemResult: action.value };
    case 'SET_MUST_DOWNLOAD_VIDEO_URLS':
      return { ...state, mustDownloadVideoUrls: action.value };
    case 'SET_NEED_DOWNLOAD_VIDEO_NUM':
      return { ...state, needDownloadVideoNum: action.value };
    case 'SET_IS_OPEN_MICROPHONE':
      return { ...state, isOpenMicrophone: action.value };
    case 'SET_INSTANT_ANSWER_INFO':
      return { ...state, instantAnswerInfo: action.value };
    case 'SET_SHOW_OPEN_MICROPHONE':
      return { ...state, showOpenMicrophone: action.value };
    case 'SET_PPT_LIST':
      return { ...state, PPTList: action.value };
    case 'SET_CURRENT_PPT':
      return { ...state, currentPPT: action.value };
    case 'SET_CLASSROOM_CHANGE_TIME':
      return { ...state, classRoomChangeTime: action.value };
    case 'SET_SHOW_LOADING':
      return { ...state, showLoading: action.value };
    case 'SET_SHOW_CONTAINER_LOADING':
      return { ...state, showContainerLoading: action.value };
    case 'SET_SHOW_BAR':
      return { ...state, showBar: action.value };
    case 'SET_SHOW_TOAST_TYPE':
      return { ...state, showToastType: action.value };
    case 'SET_SOUND_URL':
      return { ...state, soundUrl: action.value };
    case 'SET_SOUND_URL_LIST':
      return { ...state, soundUrlList: action.value };
    case 'SET_SHOW_SIGN_IN':
      return { ...state, showSignIn: action.value };
    case 'SET_IS_MUTE':
      return { ...state, isMute: action.value };
    case 'SET_TEACHER_NAME':
      return { ...state, teacherName: action.value };
    case 'SET_NEED_SIGN': {
      const { showLock } = state;
      const isMute = showLock || action.value;
      return { ...state, needSign: action.value, isMute };
    }
    case 'SET_SHOW_SIGN_SUCCESS':
      return { ...state, showSignSuccess: action.value };
    case 'SET_SHOW_LOCK': {
      const { needSign } = state;
      const isMute = needSign || action.value;
      return { ...state, showLock: action.value, isMute };
    }
    case 'SET_PARENT_STUDENT_ID': {
      return { ...state, parentStudentId: action.value };
    }
    case 'FETCH_FAILED':
      return { ...state, isRequestFailed: action.value };
    default:
      return state;
  }
};

export const Store = createContext<Context>({
  state: initialState
});

export default (props: { [key: string]: any }): ReactElement => {
  return (
    <Provider
      reducer={reducer}
      initialState={initialState}
      Store={Store}
      actions={actions}
      {...props}
    />
  );
};
